
class ApplicationController < ActionController::Base
  include SimpleCaptcha::ControllerHelpers

  before_action :set_browser_uuid
  before_action :fetch_home_data

  protect_from_forgery with: :exception
  helper_method :logined?, :current_user, :mobile?, :is_ipad?, :omniauth_authorize_path, :mask_mobile, :is_h5?, :is_admin?

  unless Rails.application.config.consider_all_requests_local
    rescue_from Exception,                            :with => :render_error
    rescue_from RuntimeError,                         :with => :render_error
    rescue_from CanCan::AccessDenied do |exception|
      redirect_to root_path, :alert => '你没有权限访问该页面'
    end
    rescue_from ActiveRecord::RecordNotFound,         :with => :render_not_found
    rescue_from ActionController::RoutingError,       :with => :render_not_found
    rescue_from ActionController::UnknownController,  :with => :render_not_found
    rescue_from AbstractController::ActionNotFound,      :with => :render_not_found
    rescue_from Errno::ECONNREFUSED,                  :with => :render_error
  end

  MOBILE_USER_AGENTS =  'palm|blackberry|nokia|phone|midp|mobi|symbian|chtml|ericsson|minimo|' +
                        'audiovox|motorola|samsung|telit|upg1|windows ce|ucweb|astel|plucker|' +
                        'x320|x240|j2me|sgh|portable|sprint|docomo|kddi|softbank|android|mmp|' +
                        'pdxgw|netfront|xiino|vodafone|portalmmm|sagem|mot-|sie-|ipod|up\\.b|' +
                        'webos|amoi|novarra|cdm|alcatel|pocket|iphone|mobileexplorer|mobile'
  def mobile?
    agent_str = request.user_agent.to_s.downcase
    return false if agent_str =~ /ipad/
    agent_str =~ Regexp.new(MOBILE_USER_AGENTS)
  end

  def is_ipad?
    request.user_agent.to_s.downcase =~ /ipad/
  end

  def is_admin?
    current_user && current_user.has_role?(:admin)
  end

  def admin_filter
    unless current_user.has_role?(:admin)
      redirect_to root_url
    end
  end

  def mask_mobile mobile
    mobile.blank? ? '' : mobile.gsub(/^(\d{3})\d+(\d{4})/, '\1****\2')
  end

  def omniauth_authorize_path(provider)
    provider = provider.to_s
    strategy = "OmniAuth::Strategies::#{provider.capitalize}".constantize
    opts = strategy.default_options.client_options

    if provider == 'wechat'
      #state?
      "https://open.weixin.qq.com/connect/qrconnect?appid=#{ENV[provider.upcase+'_KEY']}&redirect_uri=#{ENV[provider.upcase+'_REDIRECT_URI']}&response_type=code&scope=snsapi_login#wechat_redirect"
    else
      "#{opts.site}#{opts.authorize_url}?client_id=#{ENV[provider.upcase+'_KEY']}&response_type=code&redirect_uri=#{ENV[provider.upcase+'_REDIRECT_URI']}"
    end
  end

  def login_welcome user
    #flash[:success] = "<span style=\"font-weight:bold;font-size:16px;\">#{user.name.present? ? user.name : mask_mobile(user.mobile)}</span>，欢迎您回来！"
  end

  def set_seo_meta(title = '', meta_description = '', meta_keywords = '')
    @page_title = "#{title}" if title.length > 0
    @meta_description = meta_description unless meta_description.nil?
    @meta_keywords = meta_keywords unless meta_keywords.nil?
  end

  def set_common_seo_meta namespace, page_name
    entry = Setting.seo[namespace][page_name]
    set_seo_meta(entry['title'], entry['description'], entry['keywords'])
  end

  def store_location(path = nil)
    session[:return_to] = path || request.fullpath
  end

  def redirect_back_or_default(default)
    redirect_to(session[:return_to] || default)
    session[:return_to] = nil
  end

  def redirect_referrer_or_default(default)
    redirect_to(request.referrer || default)
  end

  def require_logined
    unless logined?
      if request.xhr?
        render401
      else
        store_location
        redirect_to sign_in_url
      end
    end
  end

  def require_no_logined
    if logined?
      redirect_to root_url
    end
  end

  def current_user
    @current_user ||= login_from_session || login_from_cookies unless defined?(@current_user)
    @current_user
  end

  def logined?
    !!current_user
  end

  def login_as(user)
    session[:user_id] = user.id
    @current_user = user
    user.sign_in_count = user.sign_in_count + 1
    user.last_sign_in_at = user.current_sign_in_at
    user.current_sign_in_at = Time.now
    user.last_sign_in_ip = user.current_sign_in_ip
    user.current_sign_in_ip = request.remote_ip
    user.sign_in_count = user.sign_in_count + 1
    user.skip_name_validation = true
    user.save

    update_carts(user)
  end

  def login_from_session
    if session[:user_id].present?
      begin
        User.find session[:user_id]
      rescue
        session[:user_id] = nil
      end
    end
  end

  def login_from_cookies
    if cookies[:remember_token].present?
      if user = User.find_by_remember_token(cookies[:remember_token])
        session[:user_id] = user.id
        user
      else
        forget_me
        nil
      end
    end
  end

  def logout
    session.delete(:user_id)
    @current_user = nil
    forget_me
  end

  def forget_me
    cookies.delete(:remember_token)
  end

  def remember_me
    cookies[:remember_token] = {
      :value   => current_user.remember_token,
      :expires => 2.weeks.from_now,
      :httponly => true
    }
  end

  def render_success(msg = nil, data = {})
    render :json => {
      flag: true,
      message: msg.to_s
    }.merge(data)
  end

  def render_fail(msg = nil, model = nil)
    res = {
      flag: false,
      message: msg.to_s
    }

    if model
      if model.kind_of?(Hash)
        res.merge!(model)
      else
        res.merge!( errors: flatten_errors(model.errors.messages) )
      end
    end

    render :json => res
  end

  def render401
    render :json => {:error => 'Unauthorized'}, :status => 401
  end

  def render404
    render_not_found
  end

  def get_real_ip
    ip = request.ip.to_s
    ip = request.env['HTTP_X_REAL_IP'] if ip == '127.0.0.1' && request.env.has_key?('HTTP_X_REAL_IP') && request.trusted_proxy?(request.env['HTTP_X_REAL_IP'])
    ip
  end

  def fresh_when(opts = {})
    opts[:etag] ||= []
    # 保证 etag 参数是 Array 类型
    opts[:etag] = [opts[:etag]] unless opts[:etag].is_a?(Array)
    # 加入页面上直接调用的信息用于组合 etag
    opts[:etag] << current_user
    # Config 的某些信息
    #opts[:etag] << Rails.configuration.version
    #未读消息
    #opts[:etag] << Message.unread_count(current_user.try(:id))
    # 加入flash，确保当页面刷新后flash不会再出现
    opts[:etag] << flash
    # 所有 etag 保持一天
    opts[:etag] << Date.current
    super(opts)
  end

  protected

  def is_h5?
    Rails.logger.info("*"*100)
    Rails.logger.info("tracking h5 page...")
    Rails.logger.info("*"*100)
    request.user_agent =~ /#{Rails.configuration.x.api_user_agent}/
  end

  def flatten_errors(errors)
    errors.inject({}) { |res, (k,v)| res[k] = v.first; res }
  end

  def validate_content
    if Nokogiri::HTML(params[:content]).text =~ /^[[:blank:]]*$/
      render_fail("请不要发纯表情或纯空白文字")
    end
  end

  def clear_emoji
    if params[:content].present?
      params[:content] = params[:content].clear_emoji
    end
  end

  def inc_ip_count key='sign_in'
    Rails.cache.write "#{key}/#{request.remote_ip}", ip_count(key) + 1, :expires_in => 180.seconds
  end

  def ip_count key='sign_in'
    Rails.cache.read("#{key}/#{request.remote_ip}").to_i
  end

  def require_recaptcha?
    ip_count >= 3 && (!mobile?)
  end

  def generate_flash entry, msg="信息错误"
    entry.errors.empty? ? msg : entry.errors.full_messages.to_sentence
  end

  def wrap_pagination_json result
    {total_pages: result.total_pages, current_page: result.current_page, result: ActiveModelSerializers::SerializableResource.new(result)}
  end

  private

  def render_not_found(exception = nil)
    render :template => "/errors/404", :layout => false, :status => 404
  end

  def render_error(exception = nil)
    Rails.logger.debug("*********************************** 500 *********************************************")
    Rails.logger.debug("#{exception.backtrace}: \n")
    Rails.logger.debug(exception)
    Rails.logger.debug("*********************************** 500 *********************************************")
    render :template => "/errors/500", :layout => false, :status => 500
  end

  def fetch_home_data
    @categories = Category.grouped_data
    @cart_count = Cart.by_user_uuid(session[:user_uuid]).count
  end

  def set_browser_uuid
    uuid = cookies[:user_uuid]

    unless uuid
      if logined?
        uuid = current_user.uuid
      else
        uuid = RandomCode.generate_utoken
      end
    end

    update_browser_uuid uuid
  end

  def update_browser_uuid uuid
    session[:user_uuid] = cookies.permanent['user_uuid'] = uuid
  end

  def update_carts user
    Cart.where(user_uuid: session[:user_uuid]).update_all(user_uuid: user.uuid)
  end

end

